home *** CD-ROM | disk | FTP | other *** search
/ Nebula 1 / Nebula One.iso / Internet / WWW / analog / Source / hash.c < prev    next >
Encoding:
C/C++ Source or Header  |  1996-02-07  |  36.3 KB  |  1,271 lines

  1. /*** analog 1.9beta ***/
  2. /* Please read Readme.html, or http://www.statslab.cam.ac.uk/~sret1/analog/  */
  3.  
  4. #include "analhea2.h"
  5.  
  6. /*** hash.c; the functions which do all the work in the hash tables. ***/
  7.  
  8. /*** The first few are all exactly the same, adding a certain number of
  9.   requests and a certain number of bytes to the hash entry for that item,
  10.   creating a new hash entry if none existed before. ***/
  11.  
  12. extern void *xmalloc();    /* in utils.c */
  13.                            /* used in so many functions, I declare it here */
  14.  
  15. void hashadd(struct genstruct *objhead[], int hashsize, char *name, int reqs,
  16.          double bytes, flag last7q, int *totalobjs,
  17.          int *totalobjs7, int *totalnew7, flag al)
  18. {
  19.   extern int magicno();             /* in utils.c */
  20.  
  21.   int magicnumber;
  22.   flag finished;
  23.   struct genstruct *p, *lastp, *prevp;
  24.  
  25.   /* First calculate name's "magic number" */
  26.  
  27.   magicnumber = magicno(name, hashsize);
  28.  
  29.   /* Now look through the magicnumber'th list for that URL */
  30.  
  31.   finished = FALSE;
  32.   p = (objhead[magicnumber]);
  33.   lastp = p;
  34.   prevp = p;
  35.   while (p -> name != NULL && !finished) {
  36.     if (STREQ(p -> name, name)) {   /* then done */
  37.       p -> reqs += reqs;
  38.       p -> bytes += bytes;
  39.       if (last7q && !(p -> last7)) {
  40.     (*totalobjs7)++;
  41.     p -> last7 = TRUE;
  42.       }
  43.       if (!last7q && !(p -> pre7)) {
  44.     p -> pre7 = TRUE;
  45.     (*totalnew7)--;     /* Must have p -> last7, so have wrongly
  46.                    counted it as new in last 7 */
  47.       }
  48.       /* keep the list in rough (not exact) order of number of
  49.      accesses for quicker searching; particularly useful if
  50.      the HASHSIZE is set too low. */
  51.       if (p -> reqs > lastp -> reqs) {
  52.     if (prevp == lastp) {   /* iff p is 2nd in the list */
  53.       objhead[magicnumber] = p;
  54.       lastp -> next = p -> next;
  55.       p -> next = lastp;
  56.     }
  57.     else {  /* p is 3rd or later in the list */
  58.       prevp -> next = p;
  59.       lastp -> next = p -> next;
  60.       p -> next = lastp;
  61.     }
  62.       }
  63.       finished = TRUE;
  64.     }
  65.     else {      /* look at the next one */
  66.       prevp = lastp;
  67.       lastp = p;
  68.       p = p -> next;
  69.     }
  70.   }
  71.   
  72.   if (!finished) {   /* reached the end of the list without success; new one */
  73.     (*totalobjs)++;
  74.     p -> name = (char *)xmalloc((size_t)((int)strlen(name) + 1));
  75.     strcpy(p -> name, name);
  76.     p -> reqs = reqs;
  77.     p -> bytes = bytes;
  78.     p -> aliasdone = al;
  79.     if (last7q) {
  80.       (*totalobjs7)++;
  81.       (*totalnew7)++;
  82.       p -> last7 = TRUE;
  83.       p -> pre7 = FALSE;
  84.     }
  85.     else {
  86.       p -> last7 = FALSE;
  87.       p -> pre7 = TRUE;
  88.     }
  89.     p -> next = (struct genstruct *)xmalloc(sizeof(struct genstruct));
  90.     p -> next -> name = NULL;
  91.   }
  92. }
  93.  
  94. /*** The domain hashadd function is different from the others, because we
  95.   already know all domains, so there need be no clashes in the hash table. ***/
  96.  
  97. void domhashadd(char *hostn, int reqs, double bytes)
  98. {
  99.   extern int hosttodomcode();           /* in alias.c */
  100.   extern flag subdomadd();              /* in this file */
  101.   extern int magicno();                 /* in utils.c */
  102.  
  103.   extern struct domain *domainhead[], *subdomhead[], *wildsubdomhead,
  104.                        *nwildsubdomhead;
  105.   extern int debug;
  106.  
  107.   int domcode;   /* domcode is as explained at the bit where we read the
  108.             domains in. It's the equivalent of magic number for
  109.             the other hashadd functions. */
  110.   int magicnumber;  /* for subdomains */
  111.   flag finished;
  112.   struct domain *domp;
  113.   char *tempp;
  114.   char tempchar;
  115.  
  116.   domcode = hosttodomcode(hostn);
  117.   if (domcode == DOMHASHSIZE - 2 && debug >= 2)
  118.     fprintf(stderr, "U: %s\n", hostn);
  119.  
  120.   domainhead[domcode] -> reqs += reqs;
  121.   domainhead[domcode] -> bytes += bytes;
  122.  
  123.   /* Next run through the list of wild subdomains. There are two cases;
  124.      numerical and ordinary subdomains */
  125.  
  126.   if (!isdigit(hostn[(int)strlen(hostn) - 1])) {  /* non-numerical */
  127.  
  128.     for (domp = wildsubdomhead; domp -> id != NULL; domp = domp -> next) {
  129.       if(STREQ(domp -> id,
  130.            hostn + MAX((int)strlen(hostn) - (int)strlen(domp -> id), 0))) {
  131.     /* run back to just after the previous . (or the initial character) */
  132.     tempp = hostn + MAX((int)strlen(hostn) - (int)strlen(domp -> id), 0);
  133.     while (tempp != hostn && *(tempp - 1) != '.')
  134.       tempp--;
  135.     /* now add that one to the list of subdoms; it will get looked at
  136.        in the next stage */
  137.     subdomadd(tempp, "?");
  138.       }
  139.     }
  140.  
  141.     /* now run through the subdomains this hostname could belong to and see
  142.        if any of them are required to be analysed */
  143.  
  144.     tempp = strrchr(hostn, '.');  /* the final dot */
  145.  
  146.     if (tempp != NULL) {
  147.  
  148.       while (tempp != hostn) {
  149.     tempp--;
  150.     while (tempp != hostn && *(tempp - 1) != '.')
  151.       tempp--;
  152.     magicnumber = magicno(tempp, SUBDOMHASHSIZE);
  153.     finished = OFF;
  154.     for (domp = subdomhead[magicnumber]; domp -> name != NULL && !finished;
  155.          domp = domp -> next) {
  156.       if (STREQ(domp -> id, tempp)) {
  157.         domp -> reqs += reqs;
  158.         domp -> bytes += bytes;
  159.         finished = ON;
  160.       }
  161.     }
  162.       }
  163.     }
  164.  
  165.   }   /* end non-numerical subdomains; now do numerical ones */
  166.  
  167.   else {
  168.  
  169.     /* wild numerical subdomains */
  170.  
  171.     for (domp = nwildsubdomhead; domp -> id != NULL; domp = domp -> next) {
  172.       if(strncmp(domp -> id, hostn, (size_t)(int)strlen(domp -> id)) == 0) {
  173.     /* run to the next . (or the end) */
  174.     tempp = hostn + (int)strlen(domp -> id);
  175.     while (*tempp != '.' && *tempp != '\0')
  176.       tempp++;
  177.     /* now add that one to the subdoms */
  178.     tempchar = *tempp;
  179.     *tempp = '\0';   /* temporarily trucate the string after the subdom */
  180.     subdomadd(hostn, "?");
  181.     *tempp = tempchar;
  182.       }
  183.     }
  184.  
  185.     /* and now run through the ordinary subdoms for this numerical host */
  186.  
  187.     tempp = hostn + (int)strlen(hostn);
  188.  
  189.     while (tempp != hostn) {
  190.       while (tempp != hostn && *tempp != '.' && *tempp != '\0')
  191.     tempp--;
  192.       if (tempp != hostn) {
  193.     tempchar = *tempp;
  194.     *tempp = '\0';   /* temporarily trucate the string again */
  195.     magicnumber = magicno(hostn, SUBDOMHASHSIZE);
  196.     finished = OFF;
  197.     for (domp = subdomhead[magicnumber]; domp -> name != NULL && !finished;
  198.          domp = domp -> next) {
  199.       if (STREQ(domp -> id, hostn)) {
  200.         domp -> reqs += reqs;
  201.         domp -> bytes += bytes;
  202.         finished = ON;
  203.       }
  204.     }
  205.     *tempp = tempchar;
  206.     tempp--;
  207.       }
  208.     }
  209.   }
  210.  
  211. }
  212.  
  213. /*** Add a new subdomain to the list of subdomains ***/
  214.  
  215. flag subdomadd(char *id, char *name)
  216. {
  217.   extern char *reversehostname(); /* in alias.c */
  218.  
  219.   extern struct domain *subdomhead[SUBDOMHASHSIZE];
  220.   extern struct domain *wildsubdomhead, *nwildsubdomhead;
  221.  
  222.   struct domain *domp, *domnextp;
  223.   int magicnumber;
  224.   flag numeric;       /* whether it's a numerical subdomain */
  225.  
  226.   if (id[0] == '?')  /* we don't want it */
  227.     ;
  228.  
  229.   else if (id[0] == '*') {  /* put at start wild list (order doesn't matter) */
  230.     domnextp = wildsubdomhead;
  231.     wildsubdomhead = (struct domain *)xmalloc(sizeof(struct domain));
  232.     wildsubdomhead -> next = domnextp;
  233.     wildsubdomhead -> id = xmalloc((size_t)((int)strlen(id)));
  234.     strcpy(wildsubdomhead -> id, id + 1);
  235.   }
  236.  
  237.   else if (id[(int)strlen(id) - 1] == '*') {
  238.     domnextp = nwildsubdomhead;
  239.     nwildsubdomhead = (struct domain *)xmalloc(sizeof(struct domain));
  240.     nwildsubdomhead -> next = domnextp;
  241.     nwildsubdomhead -> id = xmalloc((size_t)((int)strlen(id)));
  242.     strncpy(nwildsubdomhead -> id, id, (size_t)((int)strlen(id) - 1));
  243.     *(nwildsubdomhead -> id + (int)strlen(id) - 1) = '\0';
  244.   }
  245.  
  246.   else if (id[0] == '%') {   /* token representing "all numerical domains" */
  247.     domnextp = nwildsubdomhead;
  248.     nwildsubdomhead = (struct domain *)xmalloc(sizeof(struct domain));
  249.     nwildsubdomhead -> next = domnextp;
  250.     nwildsubdomhead -> id = xmalloc(1);
  251.     nwildsubdomhead -> id[0] = '\0';
  252.   }
  253.  
  254.   else {
  255.     magicnumber = magicno(id, SUBDOMHASHSIZE);
  256.     domp = subdomhead[magicnumber];
  257.  
  258.     /* look through that list and slot it in alphabetically
  259.        (for quicker finding than random if it's repeated later) */
  260.  
  261.     if (!isdigit(id[(int)strlen(id) - 1])) {
  262.       reversehostname(id);
  263.       numeric = FALSE;
  264.     }
  265.     else
  266.       numeric = TRUE;
  267.  
  268.     /* if it goes at the beginning of the list, slot it in there */
  269.     if (domp -> name == NULL || strcmp(domp -> revid, id) > 0) {
  270.       domnextp = domp;
  271.       domp = (struct domain *)xmalloc(sizeof(struct domain));
  272.       subdomhead[magicnumber] = domp;
  273.       domp -> revid = xmalloc((size_t)((int)strlen(id) + 1));
  274.       strcpy(domp -> revid, id);
  275.       domp -> id = xmalloc((size_t)((int)strlen(id) + 1));
  276.       if (!numeric)
  277.     reversehostname(id);
  278.       strcpy(domp -> id, id);
  279.       domp -> name = xmalloc((size_t)((int)strlen(name) + 1));
  280.       strcpy(domp -> name, name);
  281.       domp -> reqs = 0;
  282.       domp -> bytes = 0;
  283.       domp -> next = domnextp;
  284.       return(TRUE);
  285.     }
  286.     else if (strcmp(domp -> revid, id) == 0) {
  287.       if (!numeric)
  288.     reversehostname(id);
  289.       return(FALSE);
  290.     }
  291.     else {   /* run to right place in alphabet */
  292.       while (domp -> next -> name != NULL &&
  293.          strcmp(domp -> next -> revid, id) < 0)
  294.     domp = domp -> next;
  295.       if (domp -> next -> name != NULL &&
  296.       strcmp(domp -> next -> revid, id) == 0) {
  297.     if (!numeric)
  298.       reversehostname(id);   /* so as to leave it unchanged on exit */
  299.     return(FALSE);
  300.       }
  301.       else {
  302.     domnextp = domp -> next;
  303.     domp -> next = (struct domain *)xmalloc(sizeof(struct domain));
  304.     domp = domp -> next;
  305.     domp -> revid = xmalloc((size_t)((int)strlen(id) + 1));
  306.     strcpy(domp -> revid, id);
  307.     domp -> id = xmalloc((size_t)((int)strlen(id) + 1));
  308.     if (!numeric)
  309.       reversehostname(id);
  310.     strcpy(domp -> id, id);
  311.     domp -> name = xmalloc((size_t)((int)strlen(name) + 1));
  312.     strcpy(domp -> name, name);
  313.     domp -> reqs = 0;
  314.     domp -> bytes = 0;
  315.     domp -> next = domnextp;
  316.     return(TRUE);
  317.       }
  318.     }
  319.   }
  320. }
  321.  
  322. /*** Check if we want a certain referer, and add it ***/
  323.  
  324. void addref(char *fromurl, char *filename, double bytes, flag last7q,
  325.         flag filemaskq)    /* last ON if extern one ON and not yet done */
  326. {
  327.   extern flag refmaskq;
  328.   extern struct genstruct *refhead[];
  329.   extern struct include *wantrefhead, *wantfilehead;
  330.  
  331.   flag wantthisone;
  332.   int tempint;
  333.  
  334.   if (refmaskq)
  335.     wantthisone = (doaliasref(fromurl) != -1) &&
  336.       included(fromurl, wantrefhead);
  337.   if (wantthisone && filemaskq)
  338.     wantthisone = included(filename, wantfilehead);
  339.   if (wantthisone)
  340.     hashadd(refhead, REFHASHSIZE, fromurl, 1, bytes, last7q,
  341.         &tempint, &tempint, &tempint, OFF);
  342. }
  343.  
  344. /*** Process a browser line, and add it to the lists ***/
  345. /* (Little to do, so we don't use a browser alias function) */
  346.  
  347. void addbrowser(char *browser, double bytes, flag last7q)
  348. {
  349.   extern flag bq, Bq;
  350.   extern struct genstruct *browhead[], *fullbrowhead[];
  351.  
  352.   char *c;
  353.   int tempint;
  354.  
  355.   /* cut (illegal) "via"s (e.g., via proxy gateway or via Harvest cache) */
  356.   if ((c = strstr(browser, " via ")) != NULL) {
  357.     while (*c != ' ' && c != browser)
  358.       c--;
  359.     *(c + 1) = '\0';
  360.   }
  361.  
  362.   /* add to full browser report */
  363.   if (Bq)
  364.     hashadd(fullbrowhead, FULLBROWHASHSIZE, browser, 1, bytes,
  365.         last7q, &tempint, &tempint, &tempint, OFF);
  366.  
  367.   if (bq) {
  368.     /* generate shortened name */
  369.     if ((c = strchr(browser, '/')) != NULL)
  370.       *c = '\0';
  371.  
  372.     /* two special cases */
  373.     if (STREQ(browser, "Mozilla"))
  374.       strcpy(browser, "Netscape");
  375.     else if (strstr(browser, "Mosaic") != NULL ||
  376.          strstr(browser, "mosaic") != NULL)
  377.       strcpy(browser, "Mosaic");
  378.  
  379.     /* and add to browser summary */
  380.     hashadd(browhead, BROWHASHSIZE, browser, 1, bytes,
  381.         last7q, &tempint, &tempint, &tempint, OFF);
  382.   }
  383. }
  384.  
  385. /*** And one to add an error to the list of errors ***/
  386.  
  387. void adderr(char *errstr)
  388. {
  389.   extern char *strtoupper();       /* in utils.c */
  390.  
  391.   extern char errs[NO_ERRS][MAXERRLENGTH];
  392.   extern int errors[NO_ERRS];
  393.   extern int debug;
  394.  
  395.   char e1[MAXLINELENGTH], e2[MAXSTRINGLENGTH];
  396.   int i;
  397.   flag done = OFF;
  398.  
  399.   for (i = 0; !done; i++) {
  400.     strcpy(e1, errstr);
  401.     strcpy(e2, errs[i]);
  402.     if (strstr(strtoupper(e1), strtoupper(e2)) != NULL) {
  403.       done = ON;
  404.       errors[i]++;
  405.       if (debug >= 2 && i == NO_ERRS - 1)  /* unknown error type */
  406.     fprintf(stderr, "E: %s\n", errstr);
  407.     }
  408.   }
  409. }
  410.  
  411. /*** Next a function to do an approx count of the number of distinct hosts ***/
  412. /* First some utilities for it */
  413.  
  414. flag approxhostfilled(char *space, int i)
  415. {    /* whether there is already a 1 at entry i of the table */
  416.   int j, k;
  417.  
  418.   j = i / 8;
  419.   k = i % 8;
  420.  
  421.   return((*(space + j) >> k) & 1);
  422. }
  423.  
  424. void approxhostfill(char *space, int i)
  425. {    /* put a 1 at entry i; ASSUMES IT IS CURRENTLY EMPTY */
  426.   int j, k;
  427.  
  428.   j = i / 8;
  429.   k = i % 8;
  430.   *(space + j) += (1 << k);
  431.  
  432. }
  433.  
  434. void approxhosthashadd(char *hostn, flag last7q)
  435. {
  436.   extern char *approxhostspace, *approxhostspace7;
  437.   extern int approxhostsize;
  438.   extern int no_hosts, no_hosts7, no_new_hosts7;
  439.   extern flag q7;
  440.  
  441.   int magicnumber1, magicnumber2, magicnumber3, magicnumber4;
  442.   flag seen1, seen2, seen3, seen4; /* whether we'd already seen that number */
  443.   flag seen17, seen27, seen37, seen47;  /* ditto in last 7 days */
  444.   flag seen, seen7;        /* whether we've seen all 4 numbers before */
  445.   register int i;
  446.   /* NB Note approxhostfill assumes empty; be careful about two equal magic
  447.      numbers for some host */
  448.  
  449.   magicnumber1 = 0;
  450.   for (i = 0; hostn[i] != '\0'; i++) {
  451.     magicnumber1 = 101 * magicnumber1 + hostn[i];
  452.     if (magicnumber1 < 0)
  453.       magicnumber1 = -magicnumber1;
  454.     while (magicnumber1 >= 8 * approxhostsize)
  455.       magicnumber1 -= 8 * approxhostsize;
  456.   }
  457.   seen1 = approxhostfilled(approxhostspace, magicnumber1);
  458.   if (!seen1 && (!q7 || !last7q))  /* if q7 only pre-last7 go here;
  459.                       if !q7 everything does */
  460.     approxhostfill(approxhostspace, magicnumber1);
  461.   if (q7) {
  462.     seen17 = approxhostfilled(approxhostspace7, magicnumber1);
  463.     if (!seen17 && last7q)
  464.       approxhostfill(approxhostspace7, magicnumber1);
  465.   }
  466.  
  467.   magicnumber2 = 0;
  468.   for (i--; i >= 0; i--) {
  469.     magicnumber2 = 101 * magicnumber2 + hostn[i];
  470.     if (magicnumber2 < 0)
  471.       magicnumber2 = -magicnumber2;
  472.     while (magicnumber2 >= 8 * approxhostsize)
  473.       magicnumber2 -= 8 * approxhostsize;
  474.   }
  475.   seen2 = approxhostfilled(approxhostspace, magicnumber2);
  476.   if (!seen2 && (!q7 || !last7q))
  477.     approxhostfill(approxhostspace, magicnumber2);
  478.   if (q7) {
  479.     seen27 = approxhostfilled(approxhostspace7, magicnumber2);
  480.     if (!seen27 && last7q)
  481.       approxhostfill(approxhostspace7, magicnumber2);
  482.   }
  483.  
  484.   magicnumber3 = 0;
  485.   for (i = 0; hostn[i] != '\0'; i++) {
  486.     magicnumber3 = 103 * magicnumber3 + hostn[i];
  487.     if (magicnumber3 < 0)
  488.       magicnumber3 = -magicnumber3;
  489.     while (magicnumber3 >= 8 * approxhostsize)
  490.       magicnumber3 -= 8 * approxhostsize;
  491.   }
  492.   seen3 = approxhostfilled(approxhostspace, magicnumber3);
  493.   if (!seen3 && (!q7 || !last7q))
  494.     approxhostfill(approxhostspace, magicnumber3);
  495.   if (q7) {
  496.     seen37 = approxhostfilled(approxhostspace7, magicnumber3);
  497.     if (!seen37 && last7q)
  498.       approxhostfill(approxhostspace7, magicnumber3);
  499.   }
  500.  
  501.   magicnumber4 = 0;
  502.   for (i--; i >= 0; i--) {
  503.     magicnumber4 = 103 * magicnumber4 + hostn[i];
  504.     if (magicnumber4 < 0)
  505.       magicnumber4 = -magicnumber4;
  506.     while (magicnumber4 >= 8 * approxhostsize)
  507.       magicnumber4 -= 8 * approxhostsize;
  508.   }
  509.   seen4 = approxhostfilled(approxhostspace, magicnumber4);
  510.   if (!seen4 && (!q7 || !last7q))
  511.     approxhostfill(approxhostspace, magicnumber4);
  512.   if (q7) {
  513.     seen47 = approxhostfilled(approxhostspace7, magicnumber4);
  514.     if (!seen47 && last7q)
  515.       approxhostfill(approxhostspace7, magicnumber4);
  516.     seen7 = seen17 && seen27 && seen37 && seen47;
  517.   }
  518.  
  519.   seen = seen1 && seen2 && seen3 && seen4;
  520.  
  521.   if (!q7) {
  522.     if (!seen)   /* new host */
  523.       ++no_hosts;
  524.   }
  525.  
  526.   else {    /* q7 */
  527.     if (!seen && !seen7) {
  528.       ++no_hosts;
  529.       if (last7q) {
  530.     ++no_hosts7;
  531.     ++no_new_hosts7;
  532.       }
  533.     }
  534.     else if (!seen && seen7 && !last7q)  /* it wasn't really a new host 7 */
  535.       --no_new_hosts7;
  536.     else if (seen && !seen7 && last7q)
  537.       ++no_hosts7;
  538.  
  539.   }    /* end else (= if q7) */
  540.  
  541. }   /* end approxhosthashadd() */
  542.  
  543. /*** Next the functions to do with dates ***/
  544. /* add to the number of requests for a particular month */
  545. void addmonthlydata(int year, int monthno, int reqs, double bytes)
  546. {
  547.   extern struct monthly *firstm, *lastm;
  548.   extern struct timestruct firsttime, lasttime;
  549.   extern flag mback;
  550.  
  551.   struct monthly *mp;
  552.   int i;
  553.  
  554.   mp = mback?lastm:firstm;
  555.  
  556.   for (i = mback?(lasttime.year - year):(year - firsttime.year); i > 0; i--)
  557.     mp = mp -> next;       /* run to the right year */
  558.  
  559.   mp -> reqs[monthno] += reqs;   /* and add to the right month in that year */
  560.   mp -> bytes[monthno] += bytes;
  561.  
  562. }
  563.  
  564. /* add to the number of requests for a particular day */
  565. void adddailydata(int year, int monthno, int date, int reqs, double bytes)
  566. {
  567.   extern struct daily *firstd, *lastd;
  568.   extern struct timestruct firsttime, lasttime;
  569.   extern flag Dback;
  570.  
  571.   struct daily *dp;
  572.   int i;
  573.  
  574.   dp = Dback?lastd:firstd;
  575.  
  576.   for (i = Dback?((lasttime.year - year) * 12 + lasttime.monthno - monthno):
  577.        ((year - firsttime.year) * 12 + monthno - firsttime.monthno);
  578.        i > 0; i--)
  579.     dp = dp -> next;       /* run to the right month */
  580.  
  581.   dp -> reqs[date - 1] += reqs;  /* and add to the right date */
  582.   dp -> bytes[date - 1] += bytes;
  583.  
  584. }
  585.  
  586. /* add to the number of requests for a particular hour */
  587. void addhourlydata(int year, int monthno, int date, int hr, int reqs,
  588.            double bytes)
  589. {
  590.   extern struct hourly *firsth, *lasth;
  591.   extern struct timestruct firsttime, lasttime;
  592.   extern flag Hback;
  593.  
  594.   struct hourly *hp;
  595.   int i;
  596.  
  597.   hp = Hback?lasth:firsth;
  598.  
  599.   for (i = Hback?minsbetween(date, monthno, year, 0, 0, lasttime.date,
  600.                  lasttime.monthno, lasttime.year, 0, 0) / 1440:
  601.        minsbetween(firsttime.date, firsttime.monthno, firsttime.year, 0, 0,
  602.            date, monthno, year, 0, 0) / 1440;
  603.        i > 0; i--)
  604.     hp = hp -> next;       /* run to the right day */
  605.  
  606.   hp -> reqs[hr] += reqs;   /* and add to the right hour */
  607.   hp -> bytes[hr] += bytes;
  608.  
  609. }
  610.  
  611. /* add to the number of requests for a particular week */
  612. void addweeklydata(int year, int monthno, int date, int reqs, double bytes)
  613. {
  614.   extern int minsbetween();     /* in utils.c */
  615.  
  616.   extern struct weekly *firstw, *lastw;
  617.   extern flag Wback;
  618.  
  619.   struct weekly *wp;
  620.  
  621.   wp = Wback?lastw:firstw;
  622.  
  623.   while(minsbetween(wp -> start.date, wp -> start.monthno, wp -> start.year,
  624.             0, 0, date, monthno, year, 0, 0) * (Wback?(-1):1)
  625.     >= (Wback?1:10080))
  626.     wp = wp -> next;   /* run to right week (when 0 <= minsbetween < 10080) */
  627.  
  628.   wp -> reqs += reqs;            /* and add to its requests */
  629.   wp -> bytes += bytes;
  630.  
  631. }
  632.  
  633. /* and a function to co-ordinate all the date cataloguing */
  634. void datehash(int year, int monthno, int date, int hr, int min,
  635.           long thistimecode, int reqs, double bytes)
  636. {
  637.   extern long timecode();        /* in utils.c */
  638.  
  639.   extern flag mq, dq, Dq, Wq, Hq;
  640.   extern flag mback, Dback, Wback, Hback;
  641.   extern struct timestruct firsttime, lasttime;
  642.   extern struct monthly *firstm, *lastm;
  643.   extern struct daily *firstd, *lastd;
  644.   extern struct weekly *firstw, *lastw;
  645.   extern struct hourly *firsth, *lasth;
  646.   extern int monthlength[];
  647.   extern int dailyreq[], hourlyreq[];
  648.   extern double dailybytes[], hourlybytes[];
  649.  
  650.   int day;
  651.   struct monthly *tempmp;
  652.   struct daily *tempdp;
  653.   struct weekly *tempwp;
  654.   struct hourly *temphp;
  655.   int i, j;
  656.  
  657.   if (mq) {
  658.  
  659.     if (year <= firsttime.year) {  /* then we might need a new lot of months */
  660.       for (i = firsttime.year - year; i > 0; i--) {
  661.     tempmp = firstm;
  662.     firstm = (struct monthly *)xmalloc(sizeof(struct monthly));
  663.     if (mback) {
  664.       tempmp -> next = firstm;
  665.       firstm -> next = NULL;
  666.     }
  667.     else
  668.       firstm -> next = tempmp;
  669.     for (j = 0; j < 12; j++) {
  670.       firstm -> reqs[j] = 0;
  671.       firstm -> bytes[j] = 0.0;
  672.     }
  673.       }
  674.       firstm -> reqs[monthno] += reqs;    /* add to this month */
  675.       firstm -> bytes[monthno] += bytes;  /* (whether or not newly created) */
  676.     }
  677.  
  678.     else if (year >= lasttime.year) {     /* similarly */
  679.       for (i = year - lasttime.year; i > 0; i--) {
  680.     tempmp = lastm;
  681.     lastm = (struct monthly *)xmalloc(sizeof(struct monthly));
  682.     if (mback)
  683.       lastm -> next = tempmp;
  684.     else {
  685.       tempmp -> next = lastm;
  686.       lastm -> next = NULL;
  687.     }
  688.     for (j = 0; j < 12; j++) {
  689.       lastm -> reqs[j] = 0;
  690.       lastm -> bytes[j] = 0.0;
  691.     }
  692.       }
  693.       lastm -> reqs[monthno] += reqs;
  694.       lastm -> bytes[monthno] += bytes;
  695.     }
  696.  
  697.     else   /* NB we will never get here if logfile in chron. order */
  698.       addmonthlydata(year, monthno, reqs, bytes);
  699.  
  700.   }   /* end if (mq) */
  701.  
  702.   if (Dq) {
  703.  
  704.     if (year * 12 + monthno <= firsttime.year * 12 + firsttime.monthno) {
  705.       /* then we might need a new lot of days */
  706.       for (i = (firsttime.year - year) * 12 +
  707.        firsttime.monthno - monthno; i > 0; i--) {
  708.     tempdp = firstd;
  709.     firstd = (struct daily *)xmalloc(sizeof(struct daily));
  710.     if (Dback) {
  711.       tempdp -> next = firstd;
  712.       firstd -> next = NULL;
  713.     }
  714.     else
  715.       firstd -> next = tempdp;
  716.     for (j = 0; j < 31; j++) {
  717.       firstd -> reqs[j] = 0;
  718.       firstd -> bytes[j] = 0.0;
  719.     }
  720.       }
  721.       firstd -> reqs[date - 1] += reqs;
  722.       firstd -> bytes[date - 1] += bytes;
  723.     }
  724.     
  725.     else
  726.       if (year * 12 + monthno >= lasttime.year * 12 + lasttime.monthno) {
  727.     for (i = (year - lasttime.year) * 12 - lasttime.monthno + monthno;
  728.          i > 0; i--) {
  729.       tempdp = lastd;
  730.       lastd = (struct daily *)xmalloc(sizeof(struct daily));
  731.       if (Dback)
  732.         lastd -> next = tempdp;
  733.       else {
  734.         tempdp -> next = lastd;
  735.         lastd -> next = NULL;
  736.       }
  737.       for (j = 0; j < 31; j++) {
  738.         lastd -> reqs[j] = 0;
  739.         lastd -> bytes[j] = 0.0;
  740.       }
  741.     }
  742.     lastd -> reqs[date - 1] += reqs;
  743.     lastd -> bytes[date - 1] += bytes;
  744.       }
  745.  
  746.       else
  747.     adddailydata(year, monthno, date, reqs, bytes);
  748.  
  749.   }   /* end if (mq) */
  750.  
  751.   if (Hq) {
  752.  
  753.     if (year * /* 12 * 31 */ 372 + monthno * 31 + date <=
  754.     firsttime.year * 372 + firsttime.monthno * 31 + firsttime.date) {
  755.       /* then we might need a new lot of hours */
  756.       for (i = minsbetween(date, monthno, year, 0, 0, firsttime.date,
  757.                firsttime.monthno, firsttime.year, 0, 0) / 1440;
  758.        i > 0; i--) {
  759.     temphp = firsth;
  760.     firsth = (struct hourly *)xmalloc(sizeof(struct hourly));
  761.     if (Hback) {
  762.       temphp -> next = firsth;
  763.       firsth -> next = NULL;
  764.     }
  765.     else
  766.       firsth -> next = temphp;
  767.     for (j = 0; j < 24; j++) {
  768.       firsth -> reqs[j] = 0;
  769.       firsth -> bytes[j] = 0.0;
  770.     }
  771.       }
  772.       firsth -> reqs[hr] += reqs;
  773.       firsth -> bytes[hr] += bytes;
  774.     }
  775.     
  776.     else
  777.       if (year * 372 + monthno * 31 + date
  778.       >= lasttime.year * 372 + lasttime.monthno * 31 + lasttime.date) {
  779.     for (i = minsbetween(lasttime.date, lasttime.monthno, lasttime.year,
  780.                  0, 0, date, monthno, year, 0, 0) / 1440;
  781.          i > 0; i--) {
  782.       temphp = lasth;
  783.       lasth = (struct hourly *)xmalloc(sizeof(struct hourly));
  784.       if (Hback)
  785.         lasth -> next = temphp;
  786.       else {
  787.         temphp -> next = lasth;
  788.         lasth -> next = NULL;
  789.       }
  790.       for (j = 0; j < 24; j++) {
  791.         lasth -> reqs[j] = 0;
  792.         lasth -> bytes[j] = 0.0;
  793.       }
  794.     }
  795.     lasth -> reqs[hr] += reqs;
  796.     lasth -> bytes[hr] += bytes;
  797.       }
  798.  
  799.       else
  800.     addhourlydata(year, monthno, date, hr, reqs, bytes);
  801.  
  802.   }   /* end if (mq) */
  803.  
  804.   if (Wq) {
  805.     if (thistimecode < firstw -> start.code) {   /* new week needed */
  806.       while (thistimecode < firstw -> start.code) {
  807.     tempwp = firstw;
  808.     firstw = (struct weekly *)xmalloc(sizeof(struct weekly));
  809.     if (Wback) {
  810.       tempwp -> next = firstw;
  811.       firstw -> next = NULL;
  812.     }
  813.     else
  814.       firstw -> next = tempwp;
  815.     firstw -> reqs = 0;
  816.     firstw -> bytes = 0.0;
  817.     firstw -> start = tempwp -> start;
  818.     firstw -> start.date -= 7;
  819.     if (firstw -> start.date <= 0) {
  820.       firstw -> start.monthno--;
  821.       if (firstw -> start.monthno == -1) {
  822.         firstw -> start.monthno = 11;
  823.         firstw -> start.year--;
  824.       }
  825.       firstw -> start.date = monthlength[firstw -> start.monthno] +
  826.         firstw -> start.date +
  827.           ISLEAPFEB(firstw -> start.monthno, firstw -> start.year);
  828.     }
  829.     firstw -> start.code = timecode(firstw -> start.date,
  830.                     firstw -> start.monthno,
  831.                     firstw -> start.year, 0, 0);
  832.       }
  833.       firstw -> reqs += reqs;
  834.       firstw -> bytes += bytes;
  835.     }
  836.  
  837.     else if (thistimecode >= lastw -> start.code) {
  838.       while (minsbetween(lastw -> start.date, lastw -> start.monthno,
  839.              lastw -> start.year, 0, 0,  /* 10080m = 1w */
  840.              date, monthno, year, 0, 0) >= 10080) {
  841.     tempwp = lastw;
  842.     lastw = (struct weekly *)xmalloc(sizeof(struct weekly));
  843.     if (Wback)
  844.       lastw -> next = tempwp;
  845.     else {
  846.       tempwp -> next = lastw;
  847.       lastw -> next = NULL;
  848.     }
  849.     lastw -> reqs = 0;
  850.     lastw -> bytes = 0.0;
  851.     lastw -> start = tempwp -> start;
  852.     lastw -> start.date += 7;
  853.     if (lastw -> start.date > monthlength[lastw -> start.monthno] +
  854.         ISLEAPFEB(lastw -> start.monthno, lastw -> start.year)) {
  855.       lastw -> start.date -= monthlength[lastw -> start.monthno] +
  856.         ISLEAPFEB(lastw -> start.monthno, lastw -> start.year);
  857.       lastw -> start.monthno++;
  858.       if (lastw -> start.monthno == 12) {
  859.         lastw -> start.monthno = 0;
  860.         lastw -> start.year++;
  861.       }
  862.     }
  863.     lastw -> start.code = timecode(lastw -> start.date,
  864.                        lastw -> start.monthno,
  865.                        lastw -> start.year, 0, 0);
  866.       }
  867.       lastw -> reqs += reqs;
  868.       lastw -> bytes += bytes;
  869.     }
  870.     
  871.     else  /* again, only used if logfile not chronological */
  872.       addweeklydata(year, monthno, date, reqs, bytes);
  873.  
  874.   }   /* end if (Wq) */
  875.  
  876.   if (dq) {
  877.     day = dayofdate(date, monthno, year);
  878.     dailyreq[day] += reqs;
  879.     dailybytes[day] += bytes;
  880.   }
  881.   hourlyreq[hr] += reqs;  /* no need to bother checking hq */
  882.   hourlybytes[hr] += bytes;
  883.                             
  884.  
  885.   if (thistimecode < firsttime.code) {
  886.     firsttime.date = date;
  887.     firsttime.monthno = monthno;
  888.     firsttime.year = year;
  889.     firsttime.hr = hr;
  890.     firsttime.min = min;
  891.     firsttime.code = thistimecode;
  892.   }
  893.  
  894.   if (thistimecode > lasttime.code) {
  895.     lasttime.date = date;
  896.     lasttime.monthno = monthno;
  897.     lasttime.year = year;
  898.     lasttime.hr = hr;
  899.     lasttime.min = min;
  900.     lasttime.code = thistimecode;
  901.   }
  902. }
  903.  
  904. /*** Now some routines to sort the various reports ready for printing ***/
  905. /* First a simple bubblesort for the errors */
  906. void errsort(int errorder[NO_ERRS])
  907. {
  908.   extern int errors[NO_ERRS];
  909.  
  910.   int i, j, tempint;
  911.  
  912.   for (i = 0; i < NO_ERRS; i++)
  913.     errorder[i] = i;
  914.  
  915.   for (i = NO_ERRS - 2; i >= 0; i--) {
  916.     for (j = 0; j <= i; j++) {
  917.       if (errors[errorder[j]] < errors[errorder[j + 1]]) {
  918.     tempint = errorder[j];
  919.     errorder[j] = errorder[j + 1];
  920.     errorder[j + 1] = tempint;
  921.       }
  922.     }
  923.   }
  924. }
  925.  
  926. double bytefloor(double bytes, char str[])
  927. {
  928.   double ans;
  929.   int last;
  930.  
  931.   last = MAX((int)strlen(str) - 1, 0);
  932.  
  933.   if (str[last] == '%') {
  934.     str[last] = '\0';
  935.     ans = bytes * atof(str) / 100.0;
  936.     str[last] = '%';
  937.   }
  938.  
  939.   else if (str[last] == 'k' || str[last] == 'K') {
  940.     str[last] = '\0';
  941.     ans = atof(str) * 1024.0;
  942.     str[last] = 'k';
  943.   }
  944.   else if (str[last] == 'M' || str[last] == 'm') {
  945.     str[last] = '\0';
  946.     ans = atof(str) * 1048576.0;
  947.     str[last] = 'M';
  948.   }
  949.   else if (str[last] == 'G' || str[last] == 'g') {
  950.     str[last] = '\0';
  951.     ans = atof(str) * 1073741824.0;
  952.     str[last] = 'G';
  953.   }
  954.   else if (str[last] == 'T' || str[last] == 't') {
  955.     str[last] = '\0';
  956.     ans = atof(str) * 1099511627776.0;
  957.     str[last] = 'T';
  958.   }
  959.  
  960.   else
  961.     ans = atof(str);
  962.  
  963.   return(ans);
  964. }
  965.  
  966. int reqfloor(int reqs, char str[])
  967. {
  968.   int ans;
  969.   int last;
  970.  
  971.   last = MAX((int)strlen(str) - 1, 0);
  972.  
  973.   if (str[last] == '%') {
  974.     str[last] = '\0';
  975.     ans = (int)((double)reqs * atof(str) / 100.0);
  976.     str[last] = '%';
  977.   }
  978.  
  979.   else
  980.     ans = atoi(str);
  981.  
  982.   return(ans);
  983. }
  984.  
  985. /* a function to sort the generic reports */
  986. struct genstruct *gensort(struct genstruct *objhead[], int hashsize,
  987.               int sortby, char minreqstr[], char minbytestr[],
  988.               flag pagesonly,  /* for URLs, list only pages? */
  989.               flag alphahost,  /* an alphabetical hostsort? */
  990.               int *maxreqs, double *maxbytes, int *maxlength)
  991. {
  992.   extern char *reversehostname();    /* in alias.c */
  993.   extern flag included();            /* in alias.c */
  994.   extern int hoststrcmp();           /* in utils.c */
  995.  
  996.   extern struct include *ispagehead;
  997.   extern double total_bytes;
  998.   extern int total_succ_reqs;
  999.  
  1000.   flag wantit = ON;    /* whether we want a particular item */
  1001.   struct genstruct *sorthead;   /* build up the sort in this list
  1002.                 (and return it at the end) */
  1003.   struct genstruct *p, *p2, *nextp, *lastp;
  1004.   int onlist;          /* the list we are processing */
  1005.   flag finished;       /* whether we've finished with a particular entry */
  1006.   double floorb = 0.0; /* floor for bytes (for byte sorting) */
  1007.   int floorr = 0;      /* floor for requests (for other sorting) */
  1008.   int outnumber;       /* the number of items to be displayed */
  1009.  
  1010.   int i;
  1011.  
  1012.  
  1013.   /* first calculate the floor */
  1014.  
  1015.   if (sortby == BYBYTES)
  1016.     floorb = bytefloor(total_bytes, minbytestr);
  1017.   else
  1018.     floorr = reqfloor(total_succ_reqs, minreqstr);
  1019.  
  1020.   outnumber = 0;
  1021.   sorthead = (struct genstruct *)xmalloc(sizeof(struct genstruct));
  1022.   sorthead -> name = NULL;           /* as marker */
  1023.   onlist = 0;                        /* the list we are on */
  1024.   p = objhead[0];                 /* starting at list 0 */
  1025.   for ( ; onlist < hashsize; p = nextp)  {
  1026.                                      /* run through all the objects */
  1027.     if (p -> name == NULL)    {   /* then finished this list */
  1028.       nextp = objhead[++onlist];  /* so look at the next list */
  1029.     }
  1030.     else {
  1031.       if (pagesonly)   /* only for URLs */  /* if not, wantit is always ON */
  1032.     wantit = included(p -> name, ispagehead);
  1033.       if ((sortby == BYBYTES && p -> bytes < floorb) ||
  1034.       (sortby != BYBYTES && p -> reqs < floorr) ||
  1035.       (!wantit)) {  /* we don't want it */
  1036.     nextp = p -> next;
  1037.       }
  1038.       else {
  1039.     outnumber++;
  1040.     *maxreqs = MAX(p -> reqs, *maxreqs);
  1041.     *maxbytes = MAX(p -> bytes, *maxbytes);
  1042.     if (alphahost) {  /* some special things for alphabetical host sort */
  1043.       *maxlength = MAX((int)strlen(p -> name), *maxlength);
  1044.       if (!isdigit(p -> name[(int)strlen(p -> name) - 1]))
  1045.         reversehostname(p -> name);
  1046.     }
  1047.     if ((sorthead -> name == NULL) ||
  1048.         (sortby == RANDOMLY) ||
  1049.         (sortby == BYBYTES && p -> bytes > sorthead -> bytes) ||
  1050.         (sortby == BYREQUESTS && p -> reqs > sorthead -> reqs) ||
  1051.         (sortby == ALPHABETICAL && !alphahost &&
  1052.          strcmp(p -> name, sorthead -> name) < 0) ||
  1053.         (sortby == ALPHABETICAL && alphahost &&
  1054.          hoststrcmp(p -> name, sorthead -> name) < 0)) {
  1055.       /* if it's before the first item currently on the list, slot it in */
  1056.       nextp = p -> next;   /* the next one we're going to look at */
  1057.       p -> next = sorthead;
  1058.       sorthead = p;
  1059.     }
  1060.     else {                   /* otherwise compare with the ones so far */
  1061.       finished = OFF;
  1062.       lastp = sorthead;
  1063.       if (floorb < 0.0)
  1064.         i = (int)ROUND(floorb);
  1065.       else if (floorr < 0)
  1066.         i = floorr;
  1067.       else
  1068.         i = 1;
  1069.       for (p2 = sorthead -> next;
  1070.            p2 -> name != NULL && (!finished) && (i++) != -1;
  1071.            p2 = p2 -> next) {
  1072.         if ((sortby == BYBYTES && p -> bytes > p2 -> bytes) ||
  1073.         (sortby == BYREQUESTS && p -> reqs > p2 -> reqs) ||
  1074.         (sortby == ALPHABETICAL && !alphahost &&
  1075.          strcmp(p -> name, p2 -> name) < 0) ||
  1076.         (sortby == ALPHABETICAL && alphahost &&
  1077.          hoststrcmp(p -> name, p2 -> name) < 0)) {
  1078.       /* if p comes before p2 in the chosen ordering, slot it in */
  1079.           nextp = p -> next;
  1080.           p -> next = p2;
  1081.           lastp -> next = p;
  1082.           finished = ON;
  1083.         }
  1084.         lastp = p2;
  1085.       }
  1086.       if (!finished) {         /* we've reached the end of the list; */
  1087.                                /* slot it in at the end */
  1088.         nextp = p -> next;
  1089.         p -> next = p2;
  1090.         p2 -> name = NULL;
  1091.         lastp -> next = p;
  1092.       }
  1093.     }
  1094.       }
  1095.     }
  1096.     
  1097.     p = nextp;   /* so, on to the next one */
  1098.  
  1099.   }        /* end for running through all objects */
  1100.  
  1101.   if (floorb < 0.0 && outnumber <= -(int)ROUND(floorb) ||
  1102.       floorr < 0 && outnumber <= -floorr) {
  1103.                           /* -ve floor & there are at most that many objects */
  1104.     strcpy(minreqstr, "0");   /* signal to output() that we are printing all */
  1105.     strcpy(minbytestr, "0");
  1106.   }
  1107.  
  1108.   return(sorthead);
  1109.  
  1110. }    /* end gensort */
  1111.  
  1112. /*** Again, the domain report is a bit different because the
  1113.    structure is stored differently ***/
  1114.  
  1115. int domsort(void)
  1116. {
  1117.   extern int onumber;
  1118.   extern struct domain *domainhead[];
  1119.   extern int domsortby;
  1120.   extern char domminreqstr[];
  1121.   extern char domminbytestr[];
  1122.   extern double total_bytes;
  1123.   extern int total_succ_reqs;
  1124.   extern int dom_max_reqs;
  1125.   extern double dom_max_bytes;
  1126.  
  1127.   int i, j, k;
  1128.   int firstdom;  /* the numerical index of the first domain; we return this */
  1129.   int domnextj;
  1130.   struct domain *domp, *domlastp;
  1131.   int floorr;
  1132.   double floorb;
  1133.   flag finished;
  1134.  
  1135.   if (domsortby == BYBYTES)
  1136.     floorb = bytefloor(total_bytes, domminbytestr);
  1137.   else
  1138.     floorr = reqfloor(total_succ_reqs, domminreqstr);
  1139.  
  1140.   onumber = 0;
  1141.   firstdom = DOMHASHSIZE - 2; /* start with unknown domains at front of list */
  1142.   if ((domsortby == BYBYTES && (domainhead[firstdom] -> reqs == 0 ||
  1143.                 domainhead[firstdom] -> bytes < floorb)) ||
  1144.       (domsortby != BYBYTES && domainhead[firstdom] -> reqs < floorr))
  1145.     /* then we don't want it; set marker */
  1146.     domainhead[firstdom] -> reqs = -1;
  1147.   dom_max_reqs = domainhead[firstdom] -> reqs;
  1148.   dom_max_bytes = domainhead[firstdom] -> bytes;
  1149.   domainhead[firstdom] -> nexti = -1;
  1150.   j = DOMHASHSIZE - 1; /* the domain we are on; start with numerical domains */
  1151.   while (j >= 0) {              /* run through all the domains */
  1152.     domp = domainhead[j];
  1153.     domnextj = domp -> nexti;
  1154.                             /* the one we're going to look at after this one */
  1155.     if (!((domsortby == BYBYTES &&
  1156.        (domp -> reqs == 0 || domp -> bytes < floorb)) ||
  1157.       (domsortby != BYBYTES && domp -> reqs < floorr))) {
  1158.                                    /* else we don't want it */
  1159.       onumber++;
  1160.       dom_max_reqs = MAX(domp -> reqs, dom_max_reqs);
  1161.       dom_max_bytes = MAX(domp -> bytes, dom_max_bytes);
  1162.       if ((domsortby == BYBYTES &&
  1163.        domp -> bytes > domainhead[firstdom] -> bytes) ||
  1164.       (domsortby == BYREQUESTS &&
  1165.        domp -> reqs > domainhead[firstdom] -> reqs) ||
  1166.       (domsortby == ALPHABETICAL &&
  1167.        strcmp(domp -> id, domainhead[firstdom] -> id) < 0)) {
  1168.     /* if it's before the first item currently on the list, slot it in */
  1169.     domp -> nexti = firstdom;
  1170.     firstdom = j;
  1171.       }
  1172.       else {        /* otherwise compare with the ones so far */
  1173.     finished = OFF;
  1174.     domlastp = domainhead[firstdom];
  1175.     if (floorb < 0.0)
  1176.       k = (int)ROUND(floorb);
  1177.     else if (floorr < 0)
  1178.       k = floorr;
  1179.     else
  1180.       k = 1;
  1181.     for (i = domainhead[firstdom] -> nexti;
  1182.          i >= 0 && (!finished) && (k++) != -1;
  1183.          i = domainhead[i] -> nexti) {
  1184.       if ((domsortby == BYBYTES &&
  1185.            domp -> bytes > domainhead[i] -> bytes) ||
  1186.           (domsortby == BYREQUESTS &&
  1187.            domp -> reqs > domainhead[i] -> reqs) ||
  1188.           (domsortby == ALPHABETICAL &&
  1189.            strcmp(domp -> id, domainhead[i] -> id) < 0)) {
  1190.         /* if domp comes before domp2 in the chosen ordering, slot it in */
  1191.         domp -> nexti = i;
  1192.         domlastp -> nexti = j;
  1193.         finished = ON;
  1194.       }
  1195.       domlastp = domainhead[i];
  1196.     }
  1197.     if (!finished) {
  1198.       domp -> nexti = -1;   /* meaning, last item on the list */
  1199.       domlastp -> nexti = j;
  1200.     }
  1201.       }
  1202.     }
  1203.     
  1204.     j = domnextj;   /* so, on to the next one */
  1205.     
  1206.   }        /* end while j >= 0 */
  1207.  
  1208.   if (floorb < 0.0 && onumber <= -(int)ROUND(floorb) ||
  1209.       floorr < 0 && onumber <= -floorr) {
  1210.     strcpy(domminreqstr, "0");
  1211.     strcpy(domminbytestr, "0");
  1212.   }
  1213.  
  1214.   return(firstdom);
  1215.  
  1216. }   /* end domsort */
  1217.  
  1218. /*** Finally, sort subdomains into the domain report ***/
  1219.  
  1220. void subdomsort(void)
  1221. {
  1222.   extern int hosttodomcode();          /* in alias.c */
  1223.   extern int hoststrcmp();             /* in utils.c */
  1224.  
  1225.   extern int subonumber;
  1226.   extern struct domain *domainhead[], *subdomhead[];
  1227.   extern int domsortby;
  1228.   extern char subdomminbytestr[], subdomminreqstr[];
  1229.   extern double total_bytes;
  1230.   extern int total_succ_reqs;
  1231.  
  1232.   struct domain *subdomp, *subdomnextp, *domp, *domnextp;
  1233.   int floorr;
  1234.   double floorb;
  1235.   int onlist;
  1236.   int domcode;
  1237.  
  1238.   if (domsortby == BYBYTES)
  1239.     floorb = bytefloor(total_bytes, subdomminbytestr);
  1240.   else
  1241.     floorr = reqfloor(total_succ_reqs, subdomminreqstr);
  1242.  
  1243.   onlist = 0;
  1244.   subonumber = 0;
  1245.   subdomp = subdomhead[0];              /* starting at list 0 */
  1246.   for ( ; onlist < SUBDOMHASHSIZE; subdomp = subdomnextp)  {
  1247.                                         /* run through all the subdoms */
  1248.     if (subdomp -> name == NULL) {      /* then finished this list */
  1249.       subdomnextp = subdomhead[++onlist];  /* so look at the next list */
  1250.     }
  1251.     else if ((domsortby == BYBYTES && subdomp -> bytes >= floorb) ||
  1252.          (domsortby != BYBYTES && subdomp -> reqs >= floorr)) {
  1253.       subdomnextp = subdomp -> next;
  1254.       subonumber++;
  1255.       domcode = hosttodomcode(subdomp -> id);
  1256.       if (domcode != DOMHASHSIZE - 2) {
  1257.     domp = domainhead[domcode];   /* now run through that domain's
  1258.                       subdomains */
  1259.     while (domp -> next -> name != NULL &&
  1260.            hoststrcmp(domp -> next -> revid, subdomp -> revid) < 0)
  1261.       domp = domp -> next;  /* run to right place in alphabet */
  1262.     domnextp = domp -> next;  /* then slot it in */
  1263.     domp -> next = subdomp;
  1264.     subdomp -> next = domnextp;
  1265.       }
  1266.     }
  1267.     else
  1268.       subdomnextp = subdomp -> next;
  1269.   }
  1270. }
  1271.